/**
 * File: ReadMapper.java
 * Created by: mhaimel
 * Created on: Mar 9, 2010
 * CVS:  $Id: ReadMapper.java 1.0 Mar 9, 2010 1:44:51 PM mhaimel Exp $
 */
package uk.ac.ebi.curtain.model.graph.curtain;

import static uk.ac.ebi.curtain.utils.ArrayHelper.castArray;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.concurrent.atomic.AtomicBoolean;

import org.apache.commons.lang.StringUtils;

import uk.ac.ebi.curtain.model.graph.Graph;
import uk.ac.ebi.curtain.model.graph.io.ReadMapper;
import uk.ac.ebi.curtain.model.graph.io.WriteMapper;

/**
 * @author mhaimel
 *
 */
public class ReadWrapperSequMapper implements WriteMapper<ReadWrapper>, ReadMapper<ReadWrapper> {

	private static final char SEP_CHAR = ';';
	private static final char SUB_SEP_CHAR = ',';
	private static final String[] header = new String[]{
		"rmId","read_categories","multiplicity","weight_sum","gap_size","repeat_markerss"};
	
	private final AtomicBoolean loadGaps = new AtomicBoolean(true);
	
	public void setLoadGaps(boolean loadGaps) {
		this.loadGaps.set(loadGaps);
	}
	
	public boolean getLoadGaps() {
		return loadGaps.get();
	}
	
	@Override
	public String[] getHeader() {
		return header;
	}

	@Override
	public Object[] toArray(ReadWrapper value) {
		return new Object[]{
				value.getId(),
				value.getCategories(),
				joinArray(value.arrayOfMultiplicity()),
				joinArray(value.arrayOfWeightSum()),
				joinArrayArray(value.arrayOfGaps()),
				joinArray(value.arrayOfRepeatMarkers()),
		};
	}

	@Override
	public int getFieldCount() {
		return header.length;
	}

	@SuppressWarnings("unchecked")
	@Override
	public ReadWrapper loadValue(String[] array, int i, Graph graph) {
		Long id = Long.valueOf(array[i++]);
		Integer cat = Integer.valueOf(array[i++]);
		ReadWrapper wrapper = new ReadWrapper(id,cat);
		Integer[] mult = splitString(array[i++], Integer.class, cat);

		Double[] weight = splitString(array[i++], Double.class, cat);
		
		Integer[][] gaps = splitGaps(array[i++],Integer.class,cat);
		
		Integer[] repeatMarkers = new Integer[2];
		
		if(array.length > i	){			
			repeatMarkers = splitString(array[i++], Integer.class, cat);
		}else{
			Arrays.fill(repeatMarkers, 0);
		}
		
		for(int j = 0; j < cat; ++j){
			Integer m = mult[j];
			if(m > 0){
				Double d = weight[j];
				Integer[] g = gaps[j];
				Integer repeat = repeatMarkers[j];
				wrapper.forceSetCategoryReadInfo(j, new CategoryReadInfo(j,d,g, m,repeat));
			}
		}
		return wrapper;
	}
	
	private Integer[][] splitGaps(String val, Class<Integer> clazz,Integer cat) {
		if(!loadGaps.get()){
			return new Integer[cat][0];
		}
		Integer[][] res = new Integer[cat][];
		String[] split = splitString(val, cat);
		for(int i = 0; i < cat; ++i){
			Integer[] arr = 
				castArray(
//					StringUtils.split(split[i], SUB_SEP_CHAR),
					split(split[i],SUB_SEP_CHAR),
					Integer.class);
			res[i] = arr;
		}
		return res;
	}
	
	private String[] split(int size, String s, char c) {
		String[] arr = new String[size];
		int cnt = 0;
		int prev = 0;
		char[] cArr = s.toCharArray();
		int len = cArr.length;
		for(int pos = 0; pos < len;++ pos){
			if(cArr[pos] == c){
				char[] tmpArr = Arrays.copyOfRange(cArr, prev, pos);
				arr[cnt++] = new String(tmpArr);
				prev = pos+1;
			}
		}
		arr[cnt++] = new String(Arrays.copyOfRange(cArr, prev, len));
		return arr;
	}
	
	private String[] split(String s, char c) {
		LinkedList<String> ll = new LinkedList<String>();
		int prev = 0;
		char[] cArr = s.toCharArray();
		int len = cArr.length;
		for(int pos = 0; pos < len;++ pos){
			if(cArr[pos] == c){
				char[] tmpArr = Arrays.copyOfRange(cArr, prev, pos);
				ll.add(new String(tmpArr));
				prev = pos+1;
			}
		}
		if(prev<len){
			char[] tmpArr = Arrays.copyOfRange(cArr, prev, len);
			ll.add(new String(tmpArr));
		}
		return ll.toArray(new String[0]);
	}

	private <T extends Number> T[] splitString(String val, Class<T> clazz, Integer cat){
		String[] split = splitString(val, cat);
		T[] arr = castArray(split, clazz);
		return arr;
	}
	
	private String[] splitString(String val, Integer expectedSize){
		String[] arr = split(expectedSize, val, SEP_CHAR); 
//			StringUtils.splitPreserveAllTokens(val, SEP_CHAR);
		int len = arr.length;
		if(!expectedSize.equals(len)){
			throw new IllegalStateException("Expected " + expectedSize + " categories, but " + len + " found in: " + val);
		}	
		return arr;
	}
	
	private String joinArray(Object[] arr){
		return StringUtils.join(arr, SEP_CHAR);
	}
	
	private String joinArrayArray(Object[][] arr){
		int len = arr.length;
		String[] tmpArr = new String[len];
		for(int i = 0; i < len; i++){
			tmpArr[i] = StringUtils.join(arr[i], SUB_SEP_CHAR);
		}
		return StringUtils.join(tmpArr, SEP_CHAR);
	}
	
}
